#include "plugin.h"
#include <stdio.h>
#include <stack>
#include <QtWidgets/QtWidgets>

std::ostream& operator<<(std::ostream& os, const QString& str) {
	   return os<<str.toLocal8Bit().constData();
}

struct Dialog: public QDialog {
	QTableWidget* table;
	void copyContent() {
		auto clipb=QGuiApplication::clipboard();
		QString s;
		QTextStream ss{&s};
		for(int i=0; i<table->rowCount(); i++) {
			ss<<table->item(i, 0)->text()<<'\t';
			ss<<table->item(i, 1)->text()<<'\t';
			ss<<table->item(i, 2)->text()<<'\t';
			ss<<table->item(i, 3)->text()<<'\n';
		}
		clipb->setText(s);
	}

	Dialog(int nrow, QWidget* par): QDialog{par} {
		auto vlayout=new QVBoxLayout{this};

		table=new QTableWidget{nrow, 4, this};
		vlayout->addWidget(table, 1);
		QStringList headers;
		headers<<"Index"<<"Name"<<"TotLength"<<"NumBranches";
		table->setHorizontalHeaderLabels(headers);

		auto hlayout=new QHBoxLayout{};
		vlayout->addLayout(hlayout, 0);
		hlayout->addStretch(1);

		auto btn=new QPushButton{"&Copy", this};
		hlayout->addWidget(btn, 0);
		btn->setDefault(true);
		connect(btn, &QPushButton::clicked, this, &Dialog::copyContent);
	}

	void setData(size_t row, size_t idx, const std::string& name, double length, int nbr) {
		table->setItem(row, 0, new QTableWidgetItem{QString::number(idx)});
		table->setItem(row, 1, new QTableWidgetItem{QString::fromStdString(name)});
		table->setItem(row, 2, new QTableWidgetItem{QString::number(length)});
		table->setItem(row, 3, new QTableWidgetItem{QString::number(nbr)});
	}
};

struct NeuronStats final: public GraphReader {
	NeuronStats(): GraphReader{} { }
	~NeuronStats() override { }
	std::string getAnnotPath(uint32_t id) {
		std::vector<const AnnotItem*> annots;
		auto n=annot(id);
		while(n) {
			annots.push_back(n);
			n=annot(n->par_idx);
		}
		if(annots.empty())
			return {};
		std::ostringstream oss;
		oss<<annots.back()->abbrev;
		annots.pop_back();
		while(!annots.empty()) {
			oss<<"\t";
			oss<<annots.back()->abbrev;
			annots.pop_back();
		}
		return oss.str();
	}

	void read(Graph gr, QWidget* par) override {
		std::vector<double> lengths(gr.trees().size(), 0.0);
		std::vector<int> branches(gr.trees().size(), 0.0);
		for(auto e: gr.edges()) {
			if(!e.tree())
				continue;
			double len=0;
			for(size_t i=1; i<e.length(); i++) {
				len+=e.points()[i].distTo(e.points()[i-1]);
			}
			lengths[e.tree().index()]+=len;
		}
		for(auto v: gr.vertices()) {
			if(!v.tree())
				continue;
			if(v==v.tree().root())
				continue;
			if(v.neighbors().size()!=1)
				continue;
			branches[v.tree().index()]+=1;
		}

		std::vector<size_t> sels;
		for(auto n: gr.trees()) {
			if(n.selected())
				sels.push_back(n.index());
		}
		if(sels.empty()) {
			if(!gr.trees().empty()) {
				Dialog dlg(gr.trees().size(), par);
				for(size_t r=0; r<gr.trees().size(); r++) {
					auto n=gr.trees()[r];
					dlg.setData(r, n.index(), n.name(), lengths[n.index()], branches[n.index()]);
				}
				dlg.exec();
			}
		} else {
			Dialog dlg(sels.size(), par);
			for(size_t r=0; r<sels.size(); r++) {
				auto n=gr.trees()[sels[r]];
				dlg.setData(r, n.index(), n.name(), lengths[n.index()], branches[n.index()]);
			}
			dlg.exec();
		}
	}
	static GraphReaderInfo info;
	static GraphReader* creater() { return new NeuronStats{}; }
};

static void simple_load(std::vector<AlgorithmInfo*>& algs, std::vector<GraphReaderInfo*>& readers) {
	readers.push_back(new GraphReaderInfo{"Neuron Statistics", 80,
			"Collect basic statistics of neurons",
			NeuronStats::creater});
}

static void simple_unload() {
}
extern "C"
{
	PLUGIN_EXPORT
		FNT_PLUGIN("NeuronStats", &simple_load, &simple_unload, "Collect basic statistics of neurons");
}

